#pragma once
#include "util.hpp"
#include "online.hpp"
#include "db.hpp"
#include <unordered_map>
#define BROADROW 15
#define BROADCOL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

typedef enum
{
    GAME_START,
    GAME_OVER
} room_statu;

class room
{
private:
    uint64_t _room_id;
    room_statu _statu;
    uint64_t _white_id;
    uint64_t _black_id;
    int _player_count;
    online_user *_online_user;
    user_table *_ut_user;
    std::vector<std::vector<int>> _broad;

public:
    room(uint64_t room_id, online_user *online_user, user_table *ut_user)
        : _room_id(room_id), _statu(GAME_START), _player_count(0), _online_user(online_user), _ut_user(ut_user), _broad(BROADROW, std::vector<int>(BROADCOL, 0))
    {
        DLOG("%lu 房间创建完成", _room_id);
    }
    ~room()
    {
        DLOG("%lu 房间销毁完成", _room_id);
    }
    // 获取房间id
    uint64_t id() { return _room_id; }
    room_statu statu() { return _statu; }
    int player_count() { return _player_count; }
    void add_white_user(uint64_t id)
    {
        _white_id = id;
        _player_count++;
    }
    void add_black_user(uint64_t id)
    {
        _black_id = id;
        _player_count++;
    }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }
    // 判断是否有获胜情况
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        DLOG("row:%d col:%d",row,col);
        int rows = row + row_off;
        int cols = col + col_off;
        int count = 1;
        while (rows < BROADROW && rows >= 0 && cols < BROADCOL && cols >= 0 && _broad[rows][cols] == color)
        {
            ++count;
            // 向后偏移
            rows += row_off;
            cols += col_off;
            DLOG("rows:%d cols:%d",rows,cols);
        }
        // 另外一个方向
        rows = row - row_off;
        cols = col - col_off;
        while (rows < BROADROW && rows >= 0 && cols < BROADCOL && cols >= 0 && _broad[rows][cols] == color)
        {
            ++count;
            // 向前偏移
            rows -= row_off;
            cols -= col_off;
            DLOG("rows:%d cols:%d",rows,cols);
        }
        DLOG("count:%d",count);
        return count >= 5;
    }
    // 判断是否胜利
    uint64_t check_win(int row, int col, int color)
    {
        if (five(row, col, 0, 1, color) || five(row, col, 1, 0, color) || five(row, col, -1, 1, color) || five(row, col, -1, -1, color))
        {
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }
    // 处理用户聊天动作
    Json::Value handler_chat(Json::Value &req)
    {
        Json::Value resp = req;
        // 查看是否有敏感词
        std::string msg = req["message"].asString();
        auto it = msg.find("菜");
        if (it != std::string::npos)
        {
            resp["result"] = false;
            resp["reason"] = "存在敏感词,不能发送";
            return resp;
        }
        // 返回信息
        resp["result"] = true;
        return resp;
    }
    // 处理用户下棋动作
    Json::Value handler_chess(Json::Value &req)
    {
        Json::Value resp = req;
        // 2.对方掉线，我方不战而胜
        uint64_t cur_id = req["uid"].asUInt64();
        if (_online_user->is_in_game_room(_white_id) == false)
        {
            resp["result"] = true;
            resp["reason"] = "运气真好，对方掉线，不战而胜";
            resp["winner"] = (Json::Int64)_white_id;
            return resp;
        }
        if (_online_user->is_in_game_room(_black_id) == false)
        {
            resp["result"] = true;
            resp["reason"] = "运气真好，对方掉线，不战而胜";
            resp["winner"] = (Json::Int64)_black_id;
            return resp;
        }
        // 3.判断走棋位置是否正确
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        if (_broad[chess_row][chess_col] != 0)
        {
            resp["result"] = false;
            resp["reason"] = "当前位置已经有棋子了";
            return resp;
        }
        int cur_color = cur_id == _white_id ? CHESS_WHITE : CHESS_BLACK;
        //记录下棋
        _broad[chess_row][chess_col] = cur_color;
        // 4.判断输赢
        DLOG("判断输赢");
        uint64_t win_id = check_win(chess_row, chess_col, cur_color);
        if (win_id != 0)
        {
            resp["reason"] = "恭喜你战胜对手，赢得比赛!";
            DLOG("游戏一方获得胜利!");
        }
        resp["result"] = true;
        resp["winner"] = (Json::Int64)win_id;
        return resp;
    }
    // 处理玩家退出动作
    void hander_exit(uint64_t uid)
    {
        // 如果是正在游戏中退出，则对方获胜，如果是结束退出就是正常退出
        Json::Value resp;
        if (_statu == GAME_START)
        {
            uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["reason"] = "对方退出，不战而胜!";
            resp["room_id"] = (Json::UInt64)_room_id;
            resp["uid"] = (Json::UInt64)uid;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] = (Json::UInt64)winner_id;
            uint64_t lose_id = winner_id == _white_id ? _black_id : _white_id;
            _ut_user->win(winner_id);
            _ut_user->lose(lose_id);
            _statu = GAME_OVER;
            broad_cast(resp);
        }
        // 玩家数量--
        --_player_count;
    }
    // 总的请求处理，通过调用不同的函数进行不同事件的处理
    void hander_request(Json::Value &req)
    {
        DLOG("进行信息处理");
        // 验证房间号是否正确
        Json::Value resp;
        uint64_t room_id = req["room_id"].asUInt64();
        if (room_id != _room_id)
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "room id is not mate";
            DLOG("房间号不匹配");
            return broad_cast(resp);
        }
        // 根据不同的请求类型来处理不同的动作
        if (req["optype"].asString() == "put_chess")
        {
            DLOG("处理下棋请求");
            resp = handler_chess(req);
            DLOG("下棋请求处理完成");
            uint64_t winner_id = resp["winner"].asUInt64();
            if (winner_id != 0)
            {
                // 有玩家获胜
                uint64_t lose_id = winner_id == _white_id ? _black_id : _white_id;
                _ut_user->win(winner_id);
                _ut_user->lose(lose_id);
                _statu = GAME_OVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            resp = handler_chat(req);
        }
        else
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "未知请求！";
        }
        DLOG("进行广播");
        return broad_cast(resp);
    }
    // 广播给所有玩家
    void broad_cast(Json::Value &resp)
    {
        // 将响应信息序列化
        DLOG("将信息广播给游戏房间中的所有人");
        std::string body;
        json_util::serialize(resp, body);
        // 把序列化字符串发送给房间中的每一个玩家
        wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
        if (wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        else{
             DLOG("房间-⽩棋玩家连接获取失败");
        }
        wsserver_t::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);
        if (bconn.get() != nullptr)
        {
            bconn->send(body);
        }
        else{
             DLOG("房间-黑棋玩家连接获取失败");
        }
    }
};

// 房间管理类
using room_ptr = std::shared_ptr<room>;

class room_manager
{
private:
    std::mutex _mutex;
    online_user *_online_user;
    user_table *_tb_user;
    uint64_t _next_rid;                            // 房间id分配计数器
    std::unordered_map<uint64_t, room_ptr> _rooms; // 房间id和房间的关联
    std::unordered_map<uint64_t, uint64_t> _users; // 用户id和房间id的关联
public:
    room_manager(online_user *online_user, user_table *tb_user)
        : _next_rid(1), _online_user(online_user), _tb_user(tb_user)
    {
        DLOG("房间管理类创建完成!");
    }
    ~room_manager() { DLOG("房间管理类即将被销毁完成!"); }
    // 为两个用户创建一个房间
    room_ptr room_create(uint64_t uid1, uint64_t uid2)
    {
        // 如果这两个用户不在游戏大厅中，那么就不能为他们创建游戏房间
        if (_online_user->is_in_game_hall(uid1) == false)
        {
            DLOG("用户%d不在游戏大厅中，创建房间失败!", uid1);
            return room_ptr();
        }
        if (_online_user->is_in_game_hall(uid2) == false)
        {
            DLOG("用户%d不在游戏大厅中，创建房间失败!", uid2);
            return room_ptr();
        }
        // 创建房间,并将用户添加到房间管理中
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new room(_next_rid, _online_user, _tb_user));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        // 将房间信息管理起来
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        //保证每次创建房间的id是不同的
        ++_next_rid;
        return rp;
    }
    // 通过房间id来查找房间
    room_ptr get_room_by_rid(uint64_t rid)
    {
        // 加锁保护，保证线程安全
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if (it == _rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }
    // 通过用户id来查找房间
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        // 通过用户id找到房间id
        auto uit = _users.find(uid);
        if (uit == _users.end())
        {
            return room_ptr();
        }
        uint64_t rid = uit->second;
        // 通过房间id找到房间
        auto rit = _rooms.find(rid);
        if (rit == _rooms.end())
        {
            return room_ptr();
        }
        return rit->second;
    }
    // 通过房间id来销毁房间
    void destroy_room(uint64_t rid)
    {
        // 通过rid得到房间信息
        room_ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr)
        {
            return;
        }
        // 通过房间信息得到用户的信息
        uint64_t uid1 = rp->get_white_user();
        uint64_t uid2 = rp->get_black_user();
        std::unique_lock<std::mutex> lock(_mutex);
        // 删除用户和房间的关联
        _users.erase(uid1);
        _users.erase(uid2);
        // 删除房间信息
        _rooms.erase(rid);
    }
    // 删除房间中指定的用户，如果用户数量为0，则销毁房间
    void delete_room_user(uint64_t uid)
    {
        // 通过rid得到房间信息
        room_ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr)
        {
            return;
        }
        // 用户退出
        rp->hander_exit(uid);
        // 当房间内全部的用户都退出时，销毁房间
        if (rp->player_count() == 0)
        {
            destroy_room(rp->id());
        }
    }
};